package com.yonyou.pmclouds.procedure.model;

import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.util.CodeNameUtils;
import com.yonyou.pmclouds.procedure.entity.ProcedureVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author xiejianyu
 * 2019-09-04
 */
@Component
public class ProcedureManager {

    /**
     * 从二维列表解析工序，默认第一行为标题行
     * 从第二行开始解析，每行解析前4列
     * @param data 二维列表数据
     * @return 根工序(树)列表
     */
    public List<ProcedureVO> generateProcedureFromExcelData(List<List<String>> data) throws BusinessException {
        List<ProcedureVO> procedureList = null;
        if (CollectionUtils.isEmpty(data)) {
            return procedureList;
        }
        //准备返回的，根工序(树)的列表
        procedureList = new ArrayList<>();
        //当前操作的行对应的根、二级、三级工序对象
        ProcedureVO root = null, second = null, third = null;
        //当前操作的行对应的根、二级、三级工序对象的子工序的列表
        List<ProcedureVO> rootChild = null, secondChild = null, thirdChild = null;
        //同级工序的名称集合
        Set<String> rootNameSet = new HashSet<>(), secondNameSet = new HashSet<>(), thirdNameSet = new HashSet<>(), fourthNameSet = new HashSet<>();

        //从第二行开始遍历
        for (int i=1; i<data.size(); i++) {
            List<String> dataRow = data.get(i);

            //处理根工序
            String rootProcedureName = dataRow.get(0);
            if (StringUtils.isEmpty(rootProcedureName)) {
                //检查当前是否有根工序信息
                if (root==null) {
                    return procedureList; //缺少根工序
                }
            } else if (root!=null&&root.getSname().equals(rootProcedureName)) {
                //还是之前的根工序，无需处理
            } else {//新根工序
                if (procedureNameIllegal(rootProcedureName)) {
                    throw new BusinessException("Excel表格第"+(i+1)+"行一级工序名称【"+rootProcedureName+"】不合规范，请检查");
                }

                if (rootNameSet.contains(rootProcedureName)) {
                    throw new BusinessException("Excel表格第"+(i+1)+"行一级工序名称【"+rootProcedureName+"】重复，请检查");
                } else {
                    rootNameSet.add(rootProcedureName);
                }

                //之前存在根工序并且有子工序
                if (root!=null&&CollectionUtils.isNotEmpty(rootChild)) {
                    root.setChildProcedure(rootChild.toArray(new ProcedureVO[0]));
                }
                //存在二级工序并且有子工序
                if (second!=null&&CollectionUtils.isNotEmpty(secondChild)) {
                    second.setChildProcedure(secondChild.toArray(new ProcedureVO[0]));
                    second  = null;
                }
                //存在三级工序并且有子工序
                if (third!=null&&CollectionUtils.isNotEmpty(thirdChild)) {
                    third.setChildProcedure(thirdChild.toArray(new ProcedureVO[0]));
                    third = null;
                }

                secondNameSet.clear();
                thirdNameSet.clear();
                fourthNameSet.clear();

                root = new ProcedureVO();
                rootChild = new ArrayList<>();
                root.setSname(rootProcedureName);
                procedureList.add(root);
            }

            //处理二级工序
            String secondProcedureName = dataRow.get(1);
            if (StringUtils.isEmpty(secondProcedureName)) {
                //检查当前是否有二级工序信息
                if (second==null) {
                    //有三级或四级工序
                    if(StringUtils.isNotEmpty(dataRow.get(2))||StringUtils.isNotEmpty(dataRow.get(3))){
                        throw new BusinessException("Excel表格第"+(i+1)+"行，空二级工序下存在下级工序，请检查");
                    } else {//此行只有一级工序
                        continue;
                    }
                }
            } else if (second!=null&&second.getSname().equals(secondProcedureName)) {
                //还是之前的二级工序，无需处理
            } else {//新二级工序
                if (procedureNameIllegal(secondProcedureName)) {
                    throw new BusinessException("Excel表格第"+(i+1)+"行二级工序名称【"+secondProcedureName+"】不合规范，请检查");
                }

                if (secondNameSet.contains(secondProcedureName)) {
                    throw new BusinessException("Excel表格第"+(i+1)+"行二级工序名称【"+secondProcedureName+"】重复，请检查");
                } else {
                    secondNameSet.add(secondProcedureName);
                }

                //如果之前存在二级工序并且有子工序
                if (second!=null&&CollectionUtils.isNotEmpty(secondChild)) {
                    second.setChildProcedure(secondChild.toArray(new ProcedureVO[0]));
                }
                //存在三级工序并且有子工序
                if (third!=null&&CollectionUtils.isNotEmpty(thirdChild)) {
                    third.setChildProcedure(thirdChild.toArray(new ProcedureVO[0]));
                    third = null;
                }

                thirdNameSet.clear();
                fourthNameSet.clear();

                second = new ProcedureVO();
                secondChild = new ArrayList<>();
                second.setSname(secondProcedureName);
                rootChild.add(second);
            }

            //处理三级工序
            String thirdProcedureName = dataRow.get(2);
            if (StringUtils.isEmpty(thirdProcedureName)) {
                //检查当前是否有三级工序信息
                if (third==null) {
                    //有四级工序
                    if(StringUtils.isNotEmpty(dataRow.get(3))){
                        throw new BusinessException("Excel表格第"+(i+1)+"行，空三级工序下存在下级工序，请检查");
                    } else {//此行只有二级工序
                        continue;
                    }
                }
            } else if (third!=null&&third.getSname().equals(thirdProcedureName)) {
                //还是之前的三级工序，无需处理
            } else {//新三级工序
                if (procedureNameIllegal(thirdProcedureName)) {
                    throw new BusinessException("Excel表格第"+(i+1)+"行三级工序名称【"+thirdProcedureName+"】不合规范，请检查");
                }

                if (thirdNameSet.contains(thirdProcedureName)) {
                    throw new BusinessException("Excel表格第"+(i+1)+"行三级工序名称【"+thirdProcedureName+"】重复，请检查");
                } else {
                    thirdNameSet.add(thirdProcedureName);
                }

                //如果之前存在三级工序并且有子工序
                if (third!=null&&CollectionUtils.isNotEmpty(thirdChild)) {
                    third.setChildProcedure(thirdChild.toArray(new ProcedureVO[0]));
                }

                fourthNameSet.clear();

                third = new ProcedureVO();
                thirdChild = new ArrayList<>();
                third.setSname(thirdProcedureName);
                secondChild.add(third);
            }

            //处理四级工序
            String fourthProcedureName = dataRow.get(3);
            if (StringUtils.isEmpty(fourthProcedureName)) {
                continue;//此行只有三级工序
            } else {
                if (procedureNameIllegal(fourthProcedureName)) {
                    throw new BusinessException("Excel表格第"+(i+1)+"行四级工序名称【"+fourthProcedureName+"】不合规范，请检查");
                }

                if (fourthNameSet.contains(fourthProcedureName)) {
                    throw new BusinessException("Excel表格第"+(i+1)+"行四级工序名称【"+fourthProcedureName+"】重复，请检查");
                } else {
                    fourthNameSet.add(fourthProcedureName);
                }

                ProcedureVO fourth = new ProcedureVO();
                fourth.setSname(fourthProcedureName);
                thirdChild.add(fourth);
            }
        }

        //遍历完成后，维护最后一行各级工序的子工序关系
        if (root!=null&&CollectionUtils.isNotEmpty(rootChild)) {
            root.setChildProcedure(rootChild.toArray(new ProcedureVO[0]));
        }
        if (second!=null&&CollectionUtils.isNotEmpty(secondChild)) {
            second.setChildProcedure(secondChild.toArray(new ProcedureVO[0]));
        }
        if (third!=null&&CollectionUtils.isNotEmpty(thirdChild)) {
            third.setChildProcedure(thirdChild.toArray(new ProcedureVO[0]));
        }

        return procedureList;
    }

    /**
     * 检查工序名称合法性
     * @param name 工序名称
     * @return true 工序名称不合规范
     */
    private boolean procedureNameIllegal(String name) {
        if (CodeNameUtils.checkNameStr(name, 64) >= 0) {
            return true;
        }
        return false;
    }
}